#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <dirent.h>
#include <sys/stat.h>
#include <sys/types.h>

#include "plat_storage_base.h"

#define PLAT_STORAGE_BASE_LINE_SIZE (1024 * 256)

#define PLST_INFO(...) fprintf(stdout, "\033[32m[PLST_INFO] %s(%d): \033[0m", __FUNCTION__, __LINE__),fprintf(stdout, __VA_ARGS__)
#define PLST_ERR(...) fprintf(stderr, "\033[31m[PLST_ERR] %s(%d): \033[0m", __FUNCTION__, __LINE__),fprintf(stderr, __VA_ARGS__)

int32_t PlatStorageBase::GetObsolutePath(const char *childPath, char *retPath, int32_t retPathSize)
{
    const char *obsolutePath = "/tmp/storage";
    if (childPath[0] == '/')
        return snprintf(retPath, retPathSize, "%s%s", obsolutePath, childPath);
    else
        return snprintf(retPath, retPathSize, "%s/%s", obsolutePath, childPath);
}

bool PlatStorageBase::IsExist(const char *path)
{
    if (IsFolder(path))
        return true;
    return access(path, F_OK) == 0 ? true : false;
}

bool PlatStorageBase::IsFolder(const char *path)
{
    DIR *dir = opendir(path);
    if (!dir)
        return false;
    closedir(dir);
    return true;
}

int32_t PlatStorageBase::Create(const char *path)
{
    if (!path)
        return -1;

    if (IsExist(path))
        return 0;

    int32_t index = 0;
    int32_t len = strlen(path);
    char *name = new char[len + 1]();

    //从上往下逐层创建文件夹
    while (index < len)
    {
        name[index] = path[index];
        if (path[++index] == '/')
        {
            //文件夹不存在,则尝试创建
            if (!IsFolder(name))
            {
                // PLST_INFO("create dir %s ...\r\n", name);
                if (mkdir(name, 0666) != 0) {
                    PLST_ERR("mkdir %s failed\r\n", name);
                    break;
                }
            }
        }
    }

    //目标是一个文件
    if (index == len && name[index - 1] != '/')
    {
        // PLST_INFO("create file %s ...\r\n", name);
        int32_t fd = open(name, O_RDWR | O_CREAT, 0666);
        if (fd > 0)
            close(fd);
        else
            PLST_ERR("open %s failed\r\n", name);
    }

    delete[] name;
    return IsExist(path) ? 0 : (-1);
}

int32_t PlatStorageBase::DeleteFolder(const char *baseName)
{
    DIR *dir;
    struct dirent *ptr;
    int32_t baseNameLen = strlen(baseName);
    int32_t fullNameLen = baseNameLen + 128;
    char *fullName;

    dir = opendir(baseName);
    if (!dir)
        return -1;

    fullName = new char[fullNameLen];
    while ((ptr = readdir(dir)) != NULL)
    {
        if(strcmp(ptr->d_name, ".") == 0 || strcmp(ptr->d_name, "..") == 0)
            continue;
        //普通文件 || 链接文件 || 目录
        else if(ptr->d_type == 8 || ptr->d_type == 10 || ptr->d_type == 4)
        {
            memset(fullName, 0, fullNameLen);
            snprintf(fullName, fullNameLen, "%s%s%s", baseName, baseName[baseNameLen - 1] == '/' ? "" : "/", ptr->d_name);
            if (ptr->d_type == 4)
            {
                if (DeleteFolder(fullName) != 0)
                    PLST_ERR("delete dir %s failed\r\n", fullName);
            }
            else
            {
                // PLST_INFO("delete file %s ...\r\n", fullName);
                if (remove(fullName) != 0)
                    PLST_ERR("delete file %s failed\r\n", fullName);
            }
        }
    }
    closedir(dir);
    delete[] fullName;

    // PLST_INFO("delete dir %s ...\r\n", baseName);
    return rmdir(baseName);
}

int32_t PlatStorageBase::Delete(const char *path)
{
    if (!path)
        return -1;
    if (IsFolder(path))
    {
        if (rmdir(path) == 0)
            return 0;
        else
            return DeleteFolder(path);
    }
    else if (access(path, F_OK) == 0)
        return remove(path);
    else
        return 0;
}

int32_t PlatStorageBase::SizeOf(const char *file)
{
    struct stat st = {0};
    int32_t ret = stat(file, &st);
    if (ret != 0)
    {
        if (ret == -1)
            PLST_ERR("file %s not found\r\n", file);
        else
            PLST_ERR("stat file %s error %d\r\n", file, ret);
        return ret > 0 ? (-ret) : ret;
    }
    return st.st_size;
}

int32_t PlatStorageBase::CopyFile(const char *distPath, const char *srcPath)
{
    int32_t fdSrc, fdDist;
    int32_t ret = 0;

    int32_t buffSize = 8192; //增大缓冲区以提高效率
    uint8_t *buff;

    do {
        if ((fdSrc = open(srcPath, O_RDONLY)) < 0)
        {
            PLST_ERR("open srcPath %s failed\r\n", srcPath);
            ret = -1;
            break;
        }

        if (access(distPath, F_OK) != 0)
        {
            if ((ret = Create(distPath)) != 0)
            {
                PLST_ERR("Create distPath %s failed\r\n", distPath);
                break;
            }
        }

        if ((fdDist = open(distPath, O_WRONLY | O_CREAT | O_TRUNC, 0666)) < 0)
        {
            PLST_ERR("open distPath %s failed\r\n", distPath);
            ret = -1;
            break;
        }

        buff = new uint8_t[buffSize];
        while ((ret = read(fdSrc, buff, buffSize)) > 0) {
            if (write(fdDist, buff, ret) != ret) {
                PLST_ERR("write distPath %s error\r\n", distPath);
                break;
            }
        }
        delete[] buff;

    } while (0);

    if (fdSrc > 0)
        close(fdSrc);
    if (fdDist > 0)
        close(fdDist);
    return ret;
}

int32_t PlatStorageBase::CopyFolder(const char *distBaseName, const char *srcBaseName)
{
    DIR *dir;
    struct dirent *ptr;
    
    int32_t srcBaseNameLen = strlen(srcBaseName);
    int32_t srcFullNameLen = srcBaseNameLen + 128;
    char *srcFullName;
    
    int32_t distBaseNameLen = strlen(distBaseName);
    int32_t distFullNameLen = distBaseNameLen + 128;
    char *distFullName;

    dir = opendir(srcBaseName);
    if (!dir)
        return -1;

    srcFullName = new char[srcFullNameLen];
    distFullName = new char[distFullNameLen];
    while ((ptr = readdir(dir)) != NULL)
    {
        if(strcmp(ptr->d_name, ".") == 0 || strcmp(ptr->d_name, "..") == 0)
            continue;
        //普通文件 || 链接文件 || 目录
        else if(ptr->d_type == 8 || ptr->d_type == 10 || ptr->d_type == 4)
        {
            memset(srcFullName, 0, srcFullNameLen);
            snprintf(srcFullName, srcFullNameLen, "%s%s%s", srcBaseName, srcBaseName[srcBaseNameLen - 1] == '/' ? "" : "/", ptr->d_name);
            memset(distFullName, 0, distFullNameLen);
            snprintf(distFullName, distFullNameLen, "%s%s%s", distBaseName, distBaseName[distBaseNameLen - 1] == '/' ? "" : "/", ptr->d_name);
            if (ptr->d_type == 4)
            {
                if (CopyFolder(distFullName, srcFullName) != 0)
                    PLST_ERR("CopyFolder dir %s to %s failed\r\n", srcFullName, distFullName);
            }
            else
            {
                // PLST_INFO("CopyFile %s to %s ...\r\n", srcFullName, distFullName);
                if (CopyFile(distFullName, srcFullName) != 0)
                    PLST_ERR("CopyFile file %s to %s failed\r\n", srcFullName, distFullName);
            }
        }
    }
    closedir(dir);

    if (!IsFolder(distBaseName))
    {
        memset(distFullName, 0, distFullNameLen);
        snprintf(distFullName, distFullNameLen, "%s%s", distBaseName, distBaseName[distBaseNameLen - 1] == '/' ? "" : "/");
        // PLST_INFO("CreateDir %s ...\r\n", distFullName);
        if (Create(distFullName) != 0)
            PLST_ERR("Create %s failed\r\n", distFullName);
    }

    delete[] srcFullName;
    delete[] distFullName;
    return 0;
}

int32_t PlatStorageBase::Copy(const char *distPath, const char *srcPath)
{
    if (!distPath || !srcPath)
        return -1;
    if (IsFolder(srcPath))
        return CopyFolder(distPath, srcPath);
    else if (access(srcPath, F_OK) == 0) {
        if (distPath[strlen(distPath) - 1] == '/')
            PLST_ERR("copy %s to %s failed, distPath format error\r\n", srcPath, distPath);
        else
            return CopyFile(distPath, srcPath);
    }
    PLST_ERR("copy %s to %s failed, nuknown file format\r\n", srcPath, distPath);
    return -1;
}

int32_t PlatStorageBase::Move(const char *distPath, const char *srcPath)
{
    if (Copy(distPath, srcPath) == 0)
        return Delete(srcPath);
    return -1;
}

int32_t PlatStorageBase::ReadLine(const char* file, char *data, int32_t size, int32_t lineIndex)
{
    char* line;
    int32_t ret = 0;
    int32_t readSize;
    int32_t lineCount = 0;

    int32_t hadEnter = 0;
    long lineStart = 0;
    long totalSize = 0;

    int32_t fd = open(file, O_RDONLY);
    if (fd < 0) {
        PLST_ERR("open %s failed\r\n", file);
        return -1;
    }

    line = new char[PLAT_STORAGE_BASE_LINE_SIZE];
    while ((readSize = read(fd, line, PLAT_STORAGE_BASE_LINE_SIZE)) > 0)
    {
        for (int32_t i = 0; i < readSize; i++)
        {
            totalSize += 1;
            if (line[i] == '\n')
            {
                if (++lineCount == lineIndex)
                {
                    //目标行数据的长度(包含\r\n)
                    ret = totalSize - lineStart;
                    //当前line缓存范围内不包含完整一行,则向前回溯
                    if (ret > i + 1)
                    {
                        //直接文件seek移动到行起始位置
                        if (lseek(fd, lineStart, SEEK_SET) < 0)
                            ret = -1;
                        else
                        {
                            //减去\n和\r之后的长度
                            ret = ret - 1 - hadEnter;
                            //拷贝还要考虑size的大小
                            ret = read(fd, data, size < ret ? size : ret);
                        }
                    }
                    //直接从当前line缓存中拷贝
                    else
                    {
                        //行数据起始位置
                        i = i + 1 - ret;
                        //减去\n和\r之后的长度
                        ret = ret - 1 - hadEnter;
                        //考虑size的大小
                        if (size < ret)
                            ret = size;
                        memcpy(data, &line[i], ret);
                    }
                    break;
                }
                lineStart = totalSize; //记录新的一行开始位置
                hadEnter = 0; //其它时候清除
            }
            else if (line[i] == '\r')
                hadEnter += 1; //连续出现\r时计数
            else
                hadEnter = 0; //其它时候清除
        }
    }

    close(fd);
    delete[] line;
    return ret;
}

int32_t PlatStorageBase::ReadAll(const char *file, char *data, int32_t size)
{
    int32_t ret = 0;
    int32_t fd = open(file, O_RDONLY);
    if (fd < 0) {
        PLST_ERR("open %s failed\r\n", file);
        return -1;
    }
    ret = read(fd, data, size);
    close(fd);
    return ret;
}

int32_t PlatStorageBase::WriteAll(const char *file, char *data, int32_t size)
{
    int32_t ret = 0;
    int32_t fd = open(file, O_WRONLY | O_CREAT | O_TRUNC, 0666);
    if (fd < 0) {
        PLST_ERR("open %s failed\r\n", file);
        return -1;
    }
    ret = write(fd, data, size);
    close(fd);
    return ret;
}

int32_t PlatStorageBase::Append(const char *file, char *data, int32_t size)
{
    int32_t ret;
    int32_t fd = open(file, O_RDWR | O_CREAT | O_APPEND, 0666);
    if (fd < 0) {
        PLST_ERR("open %s failed\r\n", file);
        return -1;
    }
    ret = write(fd, data, size);
    close(fd);
    return ret;
}

// int32_t PlatStorageBase::Append(const char *file, const char* fmt, ...)
// {
//     va_list ap;
//     char *line;
//     int32_t ret = 0;

//     int32_t fd = open(file, O_RDWR | O_CREAT | O_APPEND, 0666);
//     if (fd < 0) {
//         PLST_ERR("open %s failed\r\n", file);
//         return -1;
//     }

//     va_start(ap, fmt);
//     line = new char[PLAT_STORAGE_BASE_LINE_SIZE]();
//     ret = vsnprintf(line, PLAT_STORAGE_BASE_LINE_SIZE, fmt, ap);
//     va_end(ap);

//     if (ret > 0)
//     {
//         write(fd, line, ret);
//         close(fd);
//     }

//     delete[] line;
//     return ret;
// }
